<head><meta name="viewport" content="user-scalable=no,initial-scale=1,maximum-scale=1">
<meta charset=utf-8>
<style>
body { margin: 0px; }
canvas { width:100%; height:100%; overflow: hidden; }
</style>
<script type="text/javascript" src="../h3du_min.js"></script>
<script type="text/javascript" src="demoutil.js"></script>
<script type="text/javascript" src="shaderlib.js"></script>
</head>
<body>
<canvas id=canvas></canvas>
<div style="position:absolute;left:0;top:0">
<input id=aaenabled type=checkbox><label for=aaenabled>Enable anti-aliasing</label>
</div>
<script id="vertex" type="x-shader/x-shader">
precision mediump float;
attribute vec3 position;
varying vec2 posVar;
varying vec4 rayStartVar;
varying vec4 rayEndVar;
uniform mat4 projection;
uniform mat4 viewMatrix;

mat4 inverse( mat4 m ){
  mat4 mc=mat4(0.0);
  float dets[6];
  dets[0] = m[2][2] * m[3][3] - m[3][2] * m[2][3];
  dets[1] = m[1][2] * m[2][3] - m[2][2] * m[1][3];
  dets[2] = m[0][2] * m[1][3] - m[1][2] * m[0][3];
  dets[3] = m[1][2] * m[3][3] - m[3][2] * m[1][3];
  dets[4] = m[0][2] * m[2][3] - m[2][2] * m[0][3];
  dets[5] = m[0][2] * m[3][3] - m[3][2] * m[0][3];
  mc[0][0] = m[1][1]*dets[0] - m[2][1]*dets[3] + m[3][1]*dets[1];
  mc[0][1] = m[2][1]*dets[5] - m[0][1]*dets[0] - m[3][1]*dets[4];
  mc[0][2] = m[0][1]*dets[3] - m[1][1]*dets[5] + m[3][1]*dets[2];
  mc[0][3] = m[1][1]*dets[4] - m[2][1]*dets[2] - m[0][1]*dets[1];
  mc[1][0] = m[2][0]*dets[3] - m[1][0]*dets[0] - m[3][0]*dets[1];
  mc[1][1] = m[0][0]*dets[0] - m[2][0]*dets[5] + m[3][0]*dets[4];
  mc[1][2] = m[1][0]*dets[5] - m[0][0]*dets[3] - m[3][0]*dets[2];
  mc[1][3] = m[0][0]*dets[1] - m[1][0]*dets[4] + m[2][0]*dets[2];
  dets[0] = m[2][0]*m[3][1] - m[3][0]*m[2][1];
  dets[1] = m[1][0]*m[2][1] - m[2][0]*m[1][1];
  dets[2] = m[0][0]*m[1][1] - m[1][0]*m[0][1];
  dets[3] = m[1][0]*m[3][1] - m[3][0]*m[1][1];
  dets[4] = m[0][0]*m[2][1] - m[2][0]*m[0][1];
  dets[5] = m[0][0]*m[3][1] - m[3][0]*m[0][1];
  mc[2][0] = m[1][3]*dets[0] - m[2][3]*dets[3] + m[3][3]*dets[1];
  mc[2][1] = m[2][3]*dets[5] - m[0][3]*dets[0] - m[3][3]*dets[4];
  mc[2][2] = m[0][3]*dets[3] - m[1][3]*dets[5] + m[3][3]*dets[2];
  mc[2][3] = m[1][3]*dets[4] - m[0][3]*dets[1] - m[2][3]*dets[2];
  mc[3][0] = m[2][2]*dets[3] - m[1][2]*dets[0] - m[3][2]*dets[1];
  mc[3][1] = m[0][2]*dets[0] - m[2][2]*dets[5] + m[3][2]*dets[4];
  mc[3][2] = m[1][2]*dets[5] - m[0][2]*dets[3] - m[3][2]*dets[2];
  mc[3][3] = m[0][2]*dets[1] - m[1][2]*dets[4] + m[2][2]*dets[2];
  float invdet = 1.0/(m[0][0]*mc[0][0] + m[1][0]*mc[0][1] +
              m[2][0]*mc[0][2] + m[3][0]*mc[0][3]);
  return mc*invdet;
}

void main(){
 mat4 invVP=inverse(projection*viewMatrix);
 rayStartVar=invVP*vec4(position.x,position.y,-1.0,1.0);
 rayEndVar=invVP*vec4(position.x,position.y,1.0,1.0);
 posVar=position.xy;
 gl_Position=vec4(position,1.0);
}
</script>
<script id="fragment" type="x-shader/x-shader">
precision mediump float;
varying vec2 posVar;
varying vec4 rayStartVar;
varying vec4 rayEndVar;
uniform float antialias;
const vec2 EPS2 = vec2(0.0, 0.001);
#define GRADIENT(f,p) (vec3(f(p + EPS2.yxx) - f(p - EPS2.yxx),f(p + EPS2.xyx) - f(p - EPS2.xyx),f(p + EPS2.xxy) - f(p - EPS2.xxy)))
float sphere(vec3 p, float radius){
  return length(p)-radius;
}
float distanceField(vec3 p){
  float d1=sphere(p+vec3(0.3,0.0,0.0),0.50); // left sphere
  float d2=sphere(p-vec3(0.3,0.0,0.0),0.50); // right sphere
  float d3=dot(p,vec3(0.0,1.0,0.0))+1.0; // floor as plane
  return min(d3,min(d1,d2));
}
vec3 dfNormal(vec3 pos){
  return normalize(GRADIENT(distanceField,pos));
}
vec3 march(vec3 origin, vec3 rayDirection, float maxDistance){
 // NOTE: rayDirection should be a unit vector
 float d=0.0;
 float dres=0.0;
 vec3 pos=vec3(0.0);
 for(int i=0;i<72;i++){
  pos=origin+rayDirection*d;
  float res=distanceField(pos);
  if(res<=0.000001){
    break;
  }
  d+=res;
  if(d>maxDistance){
   break;
  }
 }
 if(d<maxDistance){
   // objects
   vec3 diffuse = vec3(1.0);
   vec3 normal = dfNormal(pos);
   vec3 v = -rayDirection;
   float ndotv = clamp(dot(normal, v),0.0,1.0);
   vec3 color = diffuse * ndotv;
   return color;
 } else {
   // background
   float c=(origin.y+1.0)*0.5;
   return vec3(c);
 }
}
void main(){
 vec3 rayStart=rayStartVar.xyz;
 vec3 rayEnd=rayEndVar.xyz;
 rayStart/=rayStartVar.w;
 rayEnd/=rayEndVar.w;
 vec3 rayDirection=rayEnd-rayStart;
 float rayLength=length(rayDirection);
 vec3 normRayDir=rayDirection/rayLength;
 vec3 ediff=vec3(0.001,-0.001,0.0)*sqrt(rayLength*0.5);
 vec3 cx;
 if(antialias>0.0){
  vec3 c=march(rayStart+ediff.xxz,normRayDir, rayLength);
  vec3 c1=march(rayStart+ediff.xyz,normRayDir, rayLength);
  vec3 c2=march(rayStart+ediff.yxz,normRayDir, rayLength);
  vec3 c3=march(rayStart+ediff.yyz,normRayDir, rayLength);
  cx=(c+c1+c2+c3)*0.25;
 } else {
  cx=march(rayStart,normRayDir, rayLength);
 }
 gl_FragColor=vec4(cx,1.0);
}
</script>

<script id="demo">
/* global H3DU */
var vertexShader = document.getElementById("vertex").textContent;
var fragmentShader = document.getElementById("fragment").textContent;

var scene = new H3DU.Scene3D(document.getElementById("canvas"));
var shader = new H3DU.ShaderInfo(vertexShader, fragmentShader);
shader.setUniformSemantic("viewMatrix", H3DU.Semantic.VIEW);
var batch = quadBatch(shader)
batch.perspectiveAspect(45, 1, 20);
batch.setLookAt([0.001, 0, -5], [0, 0, 0]);
var timer = {};
H3DU.renderLoop(function(time) {
  "use strict";
  var angle = H3DU.Math.PiTimes2 * H3DU.getTimePosition(timer, time, 6000);
  var antialias = document.getElementById("aaenabled").checked;
  var vector = [-5 * Math.sin(angle), 0, -5 * Math.cos(angle)];
  batch.setLookAt(vector);
  shader.setUniforms({"antialias":antialias ? 1.0 : 0.0});
  scene.render(batch);
});
</script>
</body>
